原始题目:剑指 Offer 30. 包含min函数的栈 (opens new window)

解题思路:

可以借助辅助栈,用来保存当前主栈中的最小值,具体如下:

首先有两个栈 S1S1S2S2S1S1负责存储压入和弹出所有元素,S2S2 负责保存当前元素中的最小值。下面通过一个例子来说明,也可以直接看结论。

30-栈的弹出压入

压入阶段

  1. 55 压入的时候,S1S1 正常压入,因为当前 S2S2 是空的,所以 55 压入 S2S2 中;
  2. 33 压入的时候,S1S1 正常压入,因为当前 S2S2 的栈顶元素 55 大于 33,所以 33 压入 S2S2
  3. 44 压入的时候,S1S1 正常压入,因为当前 S2S2 的栈顶元素 33 小于 33,所以 44 不压入 S2S2
  4. 11 压入的时候,S1S1 正常压入,因为当前 S2S2 的栈顶元素 33 大于 11,所以 11 压入 S2S2
  5. 22 压入的时候,S1S1 正常压入,因为当前 S2S2 的栈顶元素 11 小于 22,所以 22 不压入 S2S2

弹出阶段

  1. 22 弹出时,S1S1 正常弹出,因为当前 S2S2 的栈顶元素 11 不等于 22,所以 S2S2 不弹出;
  2. 11 弹出时,S1S1 正常弹出,因为当前 S2S2 的栈顶元素 11 等于 11,所以 S2S2 弹出;
  3. 44 弹出时,S1S1 正常弹出,因为当前 S2S2 的栈顶元素 33 不等于 44,所以 S2S2 不弹出;
  4. 33 弹出时,S1S1 正常弹出,因为当前 S2S2 的栈顶元素 33 等于 33,所以 S2S2 弹出;
  5. 55 弹出时,S1S1 正常弹出,因为当前 S2S2 的栈顶元素 55 等于 55,所以 S2S2 弹出;

总结

  • 压入元素 xx 时,S1S1 都是正常压入,而 S2S2 则需要判断当前的栈顶元素 aa 是否大于等于 xx,如果大于 xx,说明此时 $a $已经不是最小的了,把 xx 压入 S2S2;否则不压入 S2S2

    • 为什么是大于等于?

      如果连续压入两个最小元素,比如压入两个 00,那么 S2S2 也要压入两次 00,这是因为弹出时,是通过判断 S1S1 和 $S2 $ 栈顶元素是否相等来决定是否要弹出 S2S2 的。因此在 a=xa = x 的时候,也要将 xx 压入 S2S2

  • 弹出元素 xx 时,S1S1 都是正常弹出,而 S2S2 则需要判断当前的栈顶元素 aa 是否等于 xx,如果等于,说明 aa 是在xx 压入时,一起压入 S2S2 的,需要把 aa 弹出。否则不弹出 S2S2 栈顶元素

代码:

class MinStack {
    /**
     * mainStack 存放着所有的元素
     */
    private final Deque<Integer> mainStack;
    /**
     * minStack 的栈顶存放着 mainStack 中的最小值
     */
    private final Deque<Integer> minStack;

    /**
     * initialize your data structure here.
     */
    public MinStack() {
        mainStack = new LinkedList<>();
        minStack = new LinkedList<>();
    }

    public void push(int x) {
        mainStack.push(x);
        if (minStack.isEmpty() || minStack.peek() >= x) {
            minStack.push(x);
        }
    }

    public void pop() {
        // 如果弹出的不是最小值,那么 minStack 就不用动
        // 如果弹出的是最小值,那么 minStack 只需要把栈顶弹出即可
        if (mainStack.pop().equals(minStack.peek())) {
            minStack.pop();
        }
    }

    public int top() {
        return mainStack.isEmpty() ? -1 : mainStack.peek();
    }

    public int min() {
        return minStack.isEmpty() ? -1 : minStack.peek();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

复杂度分析

  • 时间复杂度O(1)O(1)push(), pop(), top(), min() 四个函数的时间复杂度均为常数级别。
  • 空间复杂度O(N)O(N):当共有 NN 个待入栈元素时,辅助栈 S2S2 最差情况下存储 NN 个元素,使用 O(N)O(N) 额外空间。
上次更新: 2023/10/15